﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using Snowdreamist.ComponentModel;

namespace Snowdreamist.Collection
{
    public class ConcurrentCollection<T> : ReadWriteCriticalRegion, IList<T>, ICollection<T>
    {
        /// <summary>
        /// Default ctor
        /// </summary>
        public ConcurrentCollection() : this(null) { }

        /// <summary>
        /// ctor
        /// </summary>
        /// <param name="items"></param>
        /// <param name="notifyAction"></param>
        public ConcurrentCollection(IEnumerable<T> items)
        {
            if (items != null)
                this._lst.AddRange(items);
        }

        public virtual int IndexOf(T item)
        {
            using (this.EnterReadRegion())
            {
                return this._lst.IndexOf(item);
            }
        }

        public virtual void Insert(int index, T item)
        {
            using (this.EnterWriteRegion())
            {
                this._lst.Insert(index, item);
                this.OnCollectionChanged(NotifyCollectionChangedAction.Add, new T[] { item }, index, -1);
            }
        }

        public virtual void RemoveAt(int index)
        {
            using (this.EnterWriteRegion())
            {
                var item = this._lst[index];
                this._lst.RemoveAt(index);
                this.OnCollectionChanged(NotifyCollectionChangedAction.Remove, new T[] { item }, -1, index);
            }
        }

        public virtual T this[int index]
        {
            get
            {
                using (this.EnterReadRegion())
                {
                    return this._lst[index];
                }
            }
            set
            {
                using (this.EnterWriteRegion())
                {
                    var oldItem = this._lst[index];
                    this._lst[index] = value;
                    this.OnCollectionChanged(NotifyCollectionChangedAction.Replace, new T[] { value, oldItem }, -1, -1);
                }
            }
        }

        public virtual void Add(T item)
        {
            using (this.EnterWriteRegion())
            {
                this._lst.Add(item);
                this.OnCollectionChanged(NotifyCollectionChangedAction.Add, new T[] { item }, this._lst.Count - 1, -1);
            }
        }

        public virtual void AddRange(IEnumerable<T> items)
        {
            using (this.EnterWriteRegion())
            {
                this._lst.AddRange(items);
                this.OnCollectionChanged(NotifyCollectionChangedAction.Add, items.ToArray(), -1, -1);
            }
        }

        public virtual void Clear()
        {
            using (this.EnterWriteRegion())
            {
                this._lst.Clear();
                this.OnCollectionChanged(NotifyCollectionChangedAction.Replace, null, -1, -1);
            }
        }

        public virtual bool Contains(T item)
        {
            using (this.EnterReadRegion())
            {
                return this._lst.Contains(item);
            }
        }

        public virtual void CopyTo(T[] array, int arrayIndex)
        {
            using (this.EnterReadRegion())
            {
                this._lst.CopyTo(array, arrayIndex);
            }
        }

        public virtual int Count
        {
            get { return this.Count; }
        }

        public virtual bool IsReadOnly
        {
            get { return false; }
        }

        public virtual bool Remove(T item)
        {
            using (this.EnterWriteRegion())
            {
                var index = this.IndexOf(item);
                if (index == -1)
                    return false;
                var r = this.Remove(item);
                if (r)
                    this.OnCollectionChanged(NotifyCollectionChangedAction.Remove, new T[] { item }, -1, index);
                return r;
            }
        }

        public virtual IEnumerator<T> GetEnumerator()
        {
            using (this.EnterReadRegion())
            {
                foreach (var item in this._lst)
                    yield return item;
            }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        #region protected

        /// <summary>
        /// Action = replace
        ///     changedItems.Length = 2, [0] = newItem [1] = oldItem
        /// </summary>
        /// <param name="action"></param>
        /// <param name="changedItems"></param>
        /// <param name="newIndex"></param>
        /// <param name="oldIndex"></param>
        protected virtual void OnCollectionChanged(NotifyCollectionChangedAction action, T[] changedItems, int newIndex, int oldIndex)
        {
            // nothing
        }

        #endregion protected

        #region private

        private List<T> _lst = new List<T>();

        #endregion private
    }
}